#!/bin/sh

#define file lock that avoid script running at same time 
#定义文件锁变量
LOCKFILE="/var/lock/mount-helper.lock"

#LOG='/etc/udev/mdev-mount.log'
LOG='/dev/console'
MNTOPT='-o iocharset=utf8'
#变量定义(环境变量一般使用大写，当用户自定义的变量也可以大/小写)

if [ ! -e "$LOCKFILE" ]; then
    touch "$LOCKFILE"
    chmod 777 "$LOCKFILE"
fi

#set associated fd
#exec 用于执行命令并替换当前的shell , 200 > $LOCKFILE 表示重定向该文件描述符
exec 200>"$LOCKFILE"

#try to get file lock, until get it
#flock 表示向200 这个文件描述符上锁，一般用于进程间同步
#usage  : flock -opt file/fd , 常用-opt ： -n 尝试锁定文件，如果文件已被锁定，则立即返回错误，而不是等待
# -u 解锁文件 -w 等待解锁文件，阻塞等待
while ! flock -n  200  ; do
    echo "Lock is currently held by another process, waiting..."
    sleep $(echo "scale=2; 0.001 * 100" | bc)
	# 等效于sleep  0.1 s 
done

# (e)udev compatibility
[[ -z $MDEV ]] && MDEV=$(basename $DEVNAME)
#[ ] 用于表示测试命令操作，允许在[] 中检测表达式，-z  检测变量（字符串）
#长度是否为0 ，basename 函数用于获取脚本的名称，此处用于获取DEVNAME 名称
BLACKLISTED=""
FIRST_MEDIA="hdd"
#变量定义， 默认是使用"xx" 对变量赋值， 使用` ` 赋值表示引用命令，使用' '
#赋值表示引号内的字符会被原样保留，不会被 shell 解释或修改，这意味着单引号
#内的任何字符，包括特殊字符和命令，都会被当作普通文本处理

## device information log
function print_device_info(){
    echo  >> $LOG
    echo  >> $LOG
    echo "**************************" >> $LOG
    echo  >> $LOG
    echo "Action= "$ACTION >> $LOG
    echo "Hotplug count ="$SEQNUM >> $LOG
    echo "Major= "$MAJOR >> $LOG
    echo "Mdev= "$MDEV >> $LOG
    echo "Devpath= "$DEVPATH >> $LOG
    echo "Devtype= "$DEVTYPE >> $LOG
    echo "Subsystem= "$SUBSYSTEM >> $LOG
    echo "Minor= "$MINOR >> $LOG
    echo "Physdevpath= "$PHYSDEVPATH >> $LOG
    echo "Physdevdriver= "$PHYSDEVDRIVER >> $LOG
    echo "Physdevbus= "$PHYSDEVBUS >> $LOG
    echo "Working directory= "$PWD >> $LOG
    echo  >> $LOG
}

# 通知函数，调用hotplug_helper 这个应用程序 $1 $2表示传入的参数
notify() {
	# we don't really depend on the hotplug_helper, but when it exists, call it
	if [ -x /usr/bin/hotplug_helper ] ; then
		/usr/bin/hotplug_helper $1 $2
	fi
}
#useless
if [ -z ${DEVPATH} ]; then
    #print_device_info
    #echo "Devpath is not vaild, ignore this action" >> $LOG
    #exit 0
    sleep 1
fi

# case 语句判断 变量$ ACTION （类似于switch -case）
# action 是 mount.sh 调用时用户传入的参数？
case $ACTION in
	add|"")
		ACTION="add"
		FSTYPE=`blkid /dev/${MDEV} | grep -v 'TYPE="swap"' | grep ${MDEV} | sed -e "s/.*TYPE=//" -e 's/"//g'`
		#blkid 用于检测对应文件系统的类型 ，usage: blkid -opt path    path 表示具体的路径，-opt 可缺省
		#grep  用于搜索指定文件中的内容 ： usage: grep  -opt pattern file, opt ： -v 表示输出不匹配的行（反向搜索）
		#sed 流编辑命令，usage: sed -opt script file  这里"s/xx/xx " 表示文本替换。
		#单双引号的使用：用单引号或双引号来划定字符串。如果你在字符串中要用到某种引号，可以使用另一种引号来划定字
		#符串。即: 如果需要指定显示双引号，则可以使用单引号来指定
		#FSTYPE检测文件系统的类型并检测该变量长度是否为0
		if [ -z "$FSTYPE" ] ; then
			if [ -f /usr/bin/ntfs-3g.probe ] && ! ntfs-3g.probe --readonly /dev/${MDEV} ; then
				#echo "[mdev-mount.sh] Error FSTYPE" >> $LOG
				#exit 0
				sleep 1
			fi
		fi
		# check if already mounted
		# grep 表示查找，-opt : -q 表示默认不将结果输出到stdio
		if grep -q "^/dev/${MDEV} " /proc/mounts ; then
			# Already mounted
			#echo "[mdev-mount.sh] Already mounted" >> $LOG
			flock -u 200 # release associated fd  
			exit 0
		fi
		#expr 表示使用判断表达式，可以执行具体的数学表达式，此处类似于括号 ``表示命令
		#substr 表示实现字符串分割的功能 usage ： substr string start len
		DEVCHECK=`expr substr $MDEV 1 7`
		DEVCHECK2=`expr substr $MDEV 1 3`
		# blacklisted devices
		# for 命令用于循环读取list 中的值 即变量black 里面的值循环指向list (BLACKLISTED)
		# 直到最后一个值，这里用于判断是否有黑名单，DEVCHECK == 黑名单中的值就退出
		for black in $BLACKLISTED; do
			if [ "$DEVCHECK" == "$black" ] || [ "$DEVCHECK2" == "$black" ] ; then
				#echo "[mdev-mount.sh] blacklisted devices" >> $LOG
				flock -u 200 # release associated fd  
				exit 0
			fi
		done
		DEVCHECK=`expr substr $MDEV 1 6`
		#获取基础的设备分区和分区个数
		#如某个sd 卡存在两个分区分别是mmcblkp mmcblkp1 ,PARTNUM 就会是1 
		if [ "${DEVCHECK}" == "mmcblk" ] ; then
			DEVBASE=`expr substr $MDEV 1 7`
			PARTNUM=`expr substr $MDEV 9 1`
		else
			DEVBASE=`expr substr $MDEV 1 3`
			PARTNUM=`expr substr $MDEV 4 1`
		fi
		# check for "please don't mount it" file
		if [ -f "/dev/nomount.${DEVBASE}" ] ; then
			flock -u 200 # release associated fd  
			exit 0
		fi
		# check for full-disk partition
		# /sys/block 目录下会记录多个分区的目录，这里通过判断是否存在这些分区，执行一些操作
		# 判断某个分区size是否为0 ，如果为0 不执行任何操作，并退出脚本
		if [ "${DEVBASE}" == "${MDEV}" ] ; then
			if [ -d /sys/block/${DEVBASE}/${DEVBASE}1 -o -d /sys/block/${DEVBASE}/${DEVBASE}p1 ] ; then
				# Partition detected, just tell and quit
				# notify
				#echo "[mdev-mount.sh] Partition detected, just tell and quit" >> $LOG
				flock -u 200 # release associated fd  
				exit 0
			fi
			if [ ! -f /sys/block/${DEVBASE}/size ] ; then
				# No size at all
				#echo "[mdev-mount.sh] No size at all" >> $LOG
				flock -u 200 # release associated fd  
				exit 0
			fi
			if [ `cat /sys/block/${DEVBASE}/size` == 0 ] ; then
				# empty device, bail out
				#echo "[mdev-mount.sh] empty device, bail out" >> $LOG
				flock -u 200 # release associated fd  
				exit 0
			fi
		fi
		# due to multiple calls of mdev mount only first partition as usual, others as devicename.
		# first allow fstab to determine the mountpoint
		# -o usefree：这是一个挂载选项，具体含义可能与特定的文件系统有关。在某些情况下，它可以指示挂载操作使用可用的空闲块。
		# -o iocharset=utf8：这是另一个挂载选项，指定挂载时使用的字符集为UTF-8。
		# 这在处理包含多字节字符（如中文、日文或韩文）的文件名时非常有用。
		# > /dev/null：将标准输出（stdout）重定向到 /dev/null，即丢弃所有标准输出信息。
		# 2>&1：将标准错误输出（stderr）重定向到标准输出，这意味着所有的错误信息也将被丢弃
		# 但此处没有指定mount的挂载点，会理论上返回错误
		if ! mount -o usefree ${MNTOPT} /dev/$MDEV > /dev/null 2>&1 ; then
			#echo "[mdev-mount.sh] no fstab entry, use automatic mountpoint" >> $LOG
			# no fstab entry, use automatic mountpoint
			# 变量赋值，`` 用于表示指令
			REMOVABLE=`cat /sys/block/$DEVBASE/removable`
			# readlink 用于表示获取指定文件的链接(如果该文件存在链接) 
			readlink -fn /sys/block/$DEVBASE/device | grep -qs 'pci\|ahci'
			EXTERNAL=$?
			if [ "${REMOVABLE}" -eq "0" -a $EXTERNAL -eq 0 ] ; then
				echo "[mdev-mount.sh] non removable, non external" >> $LOG
			else
				#echo "[mdev-mount.sh] removable or external" >> $LOG
				#获取存储设备的一些设备信息
				MODEL=`cat /sys/block/$DEVBASE/device/model`
				MODEL1=`cat /sys/block/$DEVBASE/device/type`
				if [ "$MODEL" == "USB CF Reader     " ]; then
					DEVICETYPE="cf"
				elif [ "$MODEL" == "Compact Flash   " ]; then
					DEVICETYPE="cf"
				elif [ "$MODEL" == "USB SD Reader   " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL" == "USB SD  Reader  " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL" == "SD/MMC          " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL" == "USB MS Reader   " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL" == "SM/xD-Picture   " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL" == "USB SM Reader   " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL" == "MS/MS-Pro       " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL1" == "SD	            " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL1" == "SD              " ]; then
					DEVICETYPE="mmc"
				elif [ "$MODEL1" == "SD" ]; then
					DEVICETYPE="mmc"
				else
					#echo "[mdev-mount.sh] USB device found" >> $LOG
					#PARTNUM 表示分区个数， [ ] 内部 -o 表示使用逻辑判断，表示对条件进行逻辑或（OR）运算
					if [ $PARTNUM -eq "1" -o $PARTNUM -eq "5" ] ; then
						#echo "[mdev-mount.sh] 1st partition found" >> $LOG
						# /proc/mounts 目录下会记录挂载文件系统的信息，如果挂载上后会有对应是信息
						if grep -q "/media/hdd" /proc/mounts ; then
							#echo "[mdev-mount.sh] /media/hdd exists" >> $LOG
							if grep -q "/media/usb" /proc/mounts ; then
								#echo "[mdev-mount.sh] /media/usb exists" >> $LOG
								DEVICETYPE=$MDEV
							else
								DEVICETYPE="usb"
							fi
						else
							# mount the first removable device on /media/hdd only when no other internal hdd is present
							DEVICETYPE="hdd"
							DEVLIST=`ls -1 /sys/block | grep "sd[a-z]\|mmcblk[0-9]"`
							for DEV in $DEVLIST; do
								DEVBASE=`expr substr $DEV 1 3`
								readlink -fn /sys/block/$DEVBASE/device | grep -qs 'pci\|ahci'
								EXTERNAL=$?
								if [ "${REMOVABLE}" -eq "0" -a $EXTERNAL -eq 0 ] ; then
									DEVICETYPE="usb"
									#echo "[mdev-mount.sh] internal sdx detected -> mount as USB" >> $LOG
									break
								fi
							done
						fi
					else
						#echo "[mdev-mount.sh] next partition $PARTNUM of USB device found" >> $LOG
						# Mount next partition as detected device
						DEVICETYPE=$MDEV
					fi
				fi
			fi
			# Use mkdir as 'atomic' action, failure means someone beat us to the punch
			# 根据上述的逻辑判断得到对应的挂载点
			MOUNTPOINT="/media/$DEVICETYPE"

			# Remove mountpoint not being used
			# -z 可用于比较字符串的长度是否为0 ，此处的作用： grep 用于查找文件中的$(mountpoint) 是否存在 
			if [ -z "`grep $MOUNTPOINT /proc/mounts`" ] ; then
				[ -d $MOUNTPOINT ] && echo "[mdev-mount.sh] rmdir $MOUNTPOINT" >> $LOG
				#find 命令查找文件，用于查找到挂载点后的文件夹后将其取消 ，usage： find [path] [expression]  
				#expression  : -type d / f 表示以目录、文件形式查找 -name 表示以名称形式查找
				# -delete ： 表示找到后取消该文件
				find $MOUNTPOINT  -type d -maxdepth 0 -delete
				[ -d $MOUNTPOINT ] && rmdir $MOUNTPOINT
			fi
			# Check if device is already mounted
			# 如果检测到具体的设备已mount ,即创建对应的文件夹
			if [ -z "`grep /dev/$MDEV /proc/mounts`" ]; then
				# ！ 表示逻辑非， mkdir 表达式返回值结果取反
				if ! mkdir $MOUNTPOINT ; then
					#echo "[mdev-mount.sh] mkdir $MOUNTPOINT failed, using /media/$MDEV" >> $LOG
					MOUNTPOINT="/media/$MDEV"
					mkdir -p $MOUNTPOINT
				fi
				# 执行将设备mount 指定挂载点（文件夹）的操作
				#if ! command ; then 表示命令执行失败(返回值为非0 ) 执行接下来的语句
				# mount -t auto 并非标准用法，最好是写入具体的文件系统类型
				if ! mount -t auto -o usefree ${MNTOPT} /dev/$MDEV "${MOUNTPOINT}" ; then
					if ! mount.exfat ${MNTOPT} /dev/$MDEV "${MOUNTPOINT}" ; then
						if [ ! -f /sbin/mount.ntfs-3g ] ; then
							echo "[mdev-mount.sh] mount failed 1" >> $LOG
							find "${MOUNTPOINT}" -type d -maxdepth 0 -delete
							[ -d $MOUNTPOINT ] && rmdir "${MOUNTPOINT}"
						elif ! mount.ntfs-3g /dev/$MDEV "${MOUNTPOINT}" ; then
							echo "[mdev-mount.sh] mount failed 1" >> $LOG
							find "${MOUNTPOINT}" -type d -maxdepth 0 -delete
							[ -d $MOUNTPOINT ] && rmdir "${MOUNTPOINT}"
						else
							notify "mount" $MOUNTPOINT
							echo "[mdev-mount.sh] mounted $MDEV on $MOUNTPOINT" >> $LOG
						fi
					else
						notify "mount" $MOUNTPOINT
						echo "[mdev-mount.sh] mounted $MDEV on $MOUNTPOINT" >> $LOG
					fi
				else
					notify "mount" $MOUNTPOINT
					echo "[mdev-mount.sh] mounted $MDEV on $MOUNTPOINT" >> $LOG
				fi
			else
				echo "[mdev-mount.sh] device $MDEV already mounted, skipping" >> $LOG
			fi
		fi
		;;
	remove)
		MOUNTPOINT=`grep "^/dev/$MDEV\s" /proc/mounts | cut -d' ' -f 2`
		if [ -z "$MOUNTPOINT" ] ; then
			flock -u 200 # release associated fd  
			exit 0
		fi

		notify "umount" $MOUNTPOINT

		while true
		do
			echo "[mdev-mount.sh] try umount $MOUNTPOINT" >> $LOG
			if umount $MOUNTPOINT ; then
				echo "[mdev-mount.sh] umounted $MOUNTPOINT" >> $LOG
				break;
			fi

			if umount /dev/$MDEV ; then
				echo "[mdev-mount.sh] umounted $MOUNTPOINT" >> $LOG
				break;
			fi
			sleep 1
		done
		if [ -z "`grep $MOUNTPOINT /proc/mounts`" ] ; then
			[ -d $MOUNTPOINT ] && echo "[mdev-mount.sh] rmdir $MOUNTPOINT" >> $LOG
			find $MOUNTPOINT  -type d -maxdepth 0 -delete
			[ -d $MOUNTPOINT ] && rmdir $MOUNTPOINT
		fi
		;;
	*)
		# Unexpected keyword
		flock -u 200 # release associated fd  
		exit 1
		;;
esac



# FAQ ： 这里有几个变量的名称具体是什么？ 